Skip to content

Conversation

@drahnr
Copy link
Contributor

@drahnr drahnr commented Nov 28, 2025

Part 2/4 of #1185 and lays the ground for #1354

⚠️ The PR is rather large, a lot of the changes affect large pieces of code across.

Intent

  • Provide the baseline for cheap querying partial storage maps, but not implement it
  • Prepare for potential deprecation of AccountInfo

core changes

  1. Remove the vault / storage_map BLOB entries from the accounts table.
  2. Add SmtForest and integrate into apply_block and State::load

significant changes in the following files:

  • crates/store/src/db/schema.rs introduces account_storage_headers and removes storage (the full serialized account storage) from accounts table
  • crates/store/src/state.rs / fn State::apply_block now updates the database, but also the lookup table of roots for the SmtForest and the entries in the forest itself, indirect lookup tables

out of scope

how to review

Take the existing TODOs into consideration, if they make sense. This will be the follow up PR.

@drahnr drahnr force-pushed the bernhard-integrate-smtforest branch 2 times, most recently from 7980bed to 771f6d7 Compare December 2, 2025 11:16
@drahnr drahnr force-pushed the bernhard-integrate-smtforest branch from 771f6d7 to b2aa54b Compare December 2, 2025 15:22
@drahnr drahnr force-pushed the bernhard-integrate-smtforest branch from b2aa54b to 2964a93 Compare December 2, 2025 15:54
Copy link
Collaborator

@Mirko-von-Leipzig Mirko-von-Leipzig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly questions; no real issues found as yet

@drahnr drahnr changed the base branch from next to bernhard-db-schema-queries December 29, 2025 23:53
@drahnr drahnr changed the title feat: [1/3] integrate smtforest, avoid ser/de of full account/vault data in database feat: [2/4] integrate smtforest, avoid ser/de of full account/vault data in database Dec 29, 2025
@drahnr drahnr force-pushed the bernhard-integrate-smtforest branch from 3bd5451 to c5a199a Compare December 30, 2025 00:53
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! I reviewed most of the non-test code and left some small comments inline.

Also, my understanding is that in this PR, we are just updating the SMT forest, but actually getting data from it would be done in the next PR, right?

@drahnr
Copy link
Contributor Author

drahnr commented Jan 6, 2026

Also, my understanding is that in this PR, we are just updating the SMT forest, but actually getting data from it would be done in the next PR, right?

That is correct. Querying and populating partial queries/responses is done in 3/4

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! I left some more comments inline - all of them should be pretty straight-forward. Once they are addressed, we should be good to merge.

Comment on lines 49 to 59
/// Retrieves the most recent vault SMT root for an account.
///
/// Returns the latest vault root entry regardless of block number.
/// Used when applying incremental deltas where we always want the previous state.
fn get_latest_vault_root(&self, account_id: AccountId) -> Word {
self.vault_roots
.range((account_id, BlockNumber::GENESIS)..)
.take_while(|((id, _), _)| *id == account_id)
.last()
.map_or_else(Self::empty_smt_root, |(_, root)| *root)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's mention in the doc comments that if a vault root for the specified account cannot be found, we return a root of an empty tree.

Also, looking at the implementation, I wonder if a better backing structure would be BTreeMap<AccountId, (BlockNumber, Word)>. This would make getting the last vault root much simpler.

Getting root for a specific block number should be pretty simple as well (we could use a binary search or even just a linear scan since we know that the number of entries will be pretty small per account ID).

Copy link
Contributor Author

@drahnr drahnr Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that works. When we'd get a request for a historical account vault, we need the Word at a specific BlockNumber in order to extract the root. The mapping hence would become BTreeMap<AccountId, Vec<(BlockNumber, Word)>> (we'd need that extra dimension in the value) but I think it'd make it more complicated than needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, you are correct - we'd need BTreeMap<AccountId, Vec<(BlockNumber, Word)>>. I think this still may be preferable (and we could create a newtype to wrap over Vec<(BlockNumber, Word)).

My main concern here is that I'm not sure what the impact of .range((account_id, BlockNumber::GENESIS)..) and .take_while() here would be? Does this mean we need to iterate over most of the entries in vault_roots?

Also, indexing on AccountId only would allow us to use a HashMap which should be quite a bit faster than BTreeMap here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a brief look, and using next_back() is O(log(n)) vs O(n), this is implemented in 3/4

Comment on lines 76 to 90
/// Retrieves the most recent storage map SMT root for an account slot.
///
/// Returns the latest storage root entry regardless of block number.
/// Used when applying incremental deltas where we always want the previous state.
fn get_latest_storage_map_root(
&self,
account_id: AccountId,
slot_name: &StorageSlotName,
) -> Word {
self.storage_roots
.range((account_id, slot_name.clone(), BlockNumber::GENESIS)..)
.take_while(|((id, name, _), _)| *id == account_id && name == slot_name)
.last()
.map_or_else(Self::empty_smt_root, |(_, root)| *root)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as above - though, here the data structure would be BTreeMap<(AccountId, StorageSlotName), (BlockNumber, Word)>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is re performance, I think we can always assume a block number and require the user to give us the latest_block_num - which would return to the original API proposed in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reply as in #1394 (comment).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also implemented in #1394 (comment) as next_back() to become much cheaper as well as bounding the upper end of the range

Base automatically changed from bernhard-db-schema-queries to next January 9, 2026 15:34
@drahnr
Copy link
Contributor Author

drahnr commented Jan 11, 2026

I am in favor to squash merge and track the remaining comments in #1498

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you!

I am in favor to squash merge and track the remaining comments in #1498

Agreed. Let's also add to #1498 moving InnerForest into InnerState struct (and probably coming up with a better naming scheme).

@drahnr drahnr merged commit 67f470e into next Jan 12, 2026
19 checks passed
@drahnr drahnr deleted the bernhard-integrate-smtforest branch January 12, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants